﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ParallelRuntimeLibrary.Collections;

namespace ParallelRuntimeLibrary.Internal
{
    /// <summary>
    /// Worker Thread.
    /// </summary>
    internal sealed class WThread
    {
        #region Veriables
        
        private Thread mThread;
        private uint mHardwareThread;

        private State mStatus;

        private Task mCurrentTask;
        private WorkStealingQueue mLocalTaskQueue;

        private WSScheduler mHost;

        private bool mFastStart;

        private object SycObject = new object();

        #endregion

        /*/////////////////////////////////////////////////////////////////////////////*/

        #region Properties
        
        internal int Id
        {
            get
            {
                return mThread == null ? -1 : mThread.ManagedThreadId;
            }
        }

        internal string CurrentTaskId
        {
            get
            {
                return (mCurrentTask != null) ? mCurrentTask.Identity : null;
            }
        }

        internal State Status
        {
            get { return mStatus; }
        }

        internal ThreadState ThreadState
        {
            get { return mThread.ThreadState; }
        }

        #endregion

        /*/////////////////////////////////////////////////////////////////////////////*/

        internal WThread(uint hardwareThread, WSScheduler host)
        {
            mHardwareThread = hardwareThread;
            mLocalTaskQueue = new WorkStealingQueue();
            mHost = host;
            mThread = new Thread(ThreadWork);
            mThread.IsBackground = true;
            mStatus = State.None;
        }

        /// <summary>
        /// Restart this thread, and cancelt the current task.
        /// I use it if there are a problem in the thread.
        /// </summary>
        internal void Restart()
        {
            lock (SycObject)
            {
                mThread = new Thread(ThreadWork);
                mThread.IsBackground = true;
                mStatus = State.None;

                mCurrentTask = null;
                mLocalTaskQueue = new WorkStealingQueue();
            }
        }

        /// <summary>
        /// If current task Id sting start like str, about it with all it's own children.
        /// </summary>
        /// <param name="str"></param>
        internal void Abort(string identity)
        {
            lock (SycObject)
            {
                if (mCurrentTask.Identity.StartsWith(identity, StringComparison.Ordinal))
                    mThread.Abort();
            }
        }

        /// <summary>
        /// Start the thread.
        /// </summary>
        internal void Start()
        {
            lock (SycObject)
            {
                if (mThread.ThreadState == (ThreadState.Unstarted | ThreadState.Background))
                    mThread.Start();
            }
        }

        internal void StartWithTask(Task task)
        {
            lock (SycObject)
            {
                mFastStart = true;
                mCurrentTask = task;

                do
                {
                    try
                    {
                        if (mThread.ThreadState == (ThreadState.Unstarted | ThreadState.Background))
                        {
                            mThread.Start();
                            return;
                        }
                        else if (mThread.ThreadState == (ThreadState.Suspended | ThreadState.Background))
                        {
                            mThread.Resume();
                            return;
                        }
                        else
                        {
                            NativeMethod.SwitchToThread();
                            if (mStatus == State.Idle)
                            {
                                mThread.Resume();
                                return;
                            }
                        }

                    }
                    catch { continue; }

                } while (true);

                
            }
        }

        /// <summary>
        /// Resume the thread work.
        /// </summary>
        internal void Resume()
        {
            lock (SycObject)
            {
                if (mThread.ThreadState == (ThreadState.Suspended | ThreadState.Background))
                    mThread.Resume();
            }
        }

        /*/////////////////////////////////////////////////////////////////////////////*/

        /// <summary>
        /// The main thread behave.
        /// </summary>
        private void ThreadWork()
        {
            NativeMethod.SetThreadIdealProcessor(NativeMethod.GetCurrentThread(), mHardwareThread);
            Task T = null;

            StartLabel:

            try
            {
                mStatus = State.Working;
                if (mFastStart)
                {
                    mFastStart = false;
                    T = mCurrentTask;
                }
                else
                    T = mLocalTaskQueue.LocalPop();
                
                if (T == null)
                    T = mHost.PopTask();

                if (T != null)
                {

                    if (!T.TryEnterIfTaskIsQueued()) goto StartLabel;

                    mCurrentTask = T;
                    if (T.Execute())
                    {
                        T.Status = TaskStatus.Completed;
                        T.MarkAsFinished();
                        
                    }

                    T.Exit();
                }
                else
                {
                    lock (SycObject)
                    {
                        mHost.mStarted = false;
                        mHost.mLazyThreads.Enqueue(this);
                        mStatus = State.Idle;
                    }
                    Thread.CurrentThread.Suspend();
                }

                goto StartLabel;
            }
            catch (ThreadAbortException)
            {
                if (T != null)
                {
                    T.Status = TaskStatus.Canceled;
                    T.Exit();
                    T.MarkAsFinished();
                }

                goto StartLabel;
            }
            catch
            {
                if (T != null)
                {
                    T.Status = TaskStatus.CompletedWithExceptions;
                    T.Exit();
                    T.MarkAsFinished();
                }

                goto StartLabel;
            }
        }

        /// <summary>
        /// The Fast Context Exeution behave. This method used for the effective waiting
        /// </summary>
        /// <param name="tasks">number of tasks that will poped from the local queue</param>
        internal void FastContextExecution(Task[] tasks, int length)
        {
            FastContextExecution(tasks, 0, length);
        }

        /// <summary>
        /// The Fast Context Exeution behave. This method used for the effective waiting
        /// </summary>
        /// <param name="tasks">number of tasks that will poped from the local queue</param>
        internal void FastContextExecution(Task[] tasks, int index, int length)
        {
            Task T = null;

            for (int i = index; i < length; i++)
            {
                T = tasks[i];
                try
                {
                    if (T.TryEnterIfTaskIsQueued())
                    {
                        mCurrentTask = T;

                        if (T.Execute())
                        {
                            T.Status = TaskStatus.Completed;
                            T.MarkAsFinished();
                        }

                        T.Exit();
                    }
                }
                catch (ThreadAbortException)
                {
                    if (T != null)
                    {
                        T.Status = TaskStatus.Canceled;
                        T.Exit();
                        T.MarkAsFinished();
                    }
                }
                catch
                {
                    if (T != null)
                    {
                        T.Status = TaskStatus.CompletedWithExceptions;
                        T.Exit();
                        T.MarkAsFinished();
                    }
                }
            }
        }

        /// <summary>
        /// The Fast Context Exeution behave. This method used for the effective waiting
        /// </summary>
        /// <param name="task"></param>
        internal void FastContextExecution(Task task)
        {
            try
            {
                if (task.TryEnterIfTaskIsQueued())
                {
                    mCurrentTask = task;

                    if (task.Execute())
                    {
                        task.Status = TaskStatus.Completed;
                        task.FinishedEvent.Set();
                    }

                    task.Exit();
                }
            }
            catch (ThreadAbortException)
            {
                if (task != null)
                {
                    task.Status = TaskStatus.Canceled;
                    task.Exit();
                    task.FinishedEvent.Set();
                }
            }
            catch
            {
                if (task != null)
                {
                    task.Status = TaskStatus.CompletedWithExceptions;
                    task.Exit();
                    task.FinishedEvent.Set();
                }
            }
        }

        /*/////////////////////////////////////////////////////////////////////////////*/

        internal void LocalPush(Task value)
        {
            mLocalTaskQueue.LocalPush(value);
        }

        internal Task StealTask()
        {
            return mLocalTaskQueue.Steal();
        }

    }
}
